 /** \file    layered_img.cpp
  * \brief   Image consisting of several independently moving layers.
  * \date    2001
  * \author  Fedor Chelnokov
  * \version $Header: /home/cscom1r4/cvsroot/cs/imglib/src/layered_img.cpp,v 1.3 2004/12/10 05:10:44 cscom1r4 Exp $
  */

#include "imglib/layered_img.h"
#include "imglib/bilinear.h"
#include "imglib/bitmap.h"
#include "imglib/warp.h"
#include "imglib/imageclass.h"
#include "imglib/convert.h"
#include "xtd/arr2d_wise_iterator.h"

#pragma warning(disable: 4244)

namespace img {

const float NO_PREIMAGE = -9999;

layered_img::layered_img(int lcount, size_point image_size,
						 size_point preimage_area, binding b) 
{
	init(lcount, image_size, preimage_area, b);
}

void layered_img::init(int lcount, size_point image_size,
					   size_point preimage_area, binding b)
{
	_layers.resize(lcount);
	_no_value = bRGB(0,0,0,0);
	_image_size = image_size;
	_preimage_area = preimage_area;
	_interpolate = true;
	_b = b;

	_image_dspl = displacement(_image_size);
	reset_motion();
}

void layered_img::reset_motion()
{
	for(size_t l = 0; l != _layers.size(); ++l) {
		_layers[l].preimage.Resize(_preimage_area.x, _preimage_area.y, Point2f(), false);
		xtd::arr2d_wise_iterator<Point2f> p( _layers[l].preimage, 0, 0 );
		for( ; p != _layers[l].preimage.end(); ++p ) {
			p->x = p.x() - _image_dspl.x;
			p->y = p.y() - _image_dspl.y;
		}
	}
}

void layered_img::next_frame( 
	const std::vector<QuadraticForm> &qf ) ///< transformations of each layer
{
	assert(qf.size() == lcount());

	for( int l = lcount()-1; l >= 0; --l )
		warpPreimage(_layers[l].preimage, qf[l], NO_PREIMAGE, _image_dspl);
}

void layered_img::next_frame(const QuadraticForm &qf)
{
	std::vector<QuadraticForm> vec;
	vec.push_back(qf);
	next_frame(vec);
}

inline Point2D<float> cnv(const point<float> & a) {
	Point2D<float> r = {a.x, a.y};
	return r;
}

void layered_img::prev_frame( 
	const std::vector<QuadraticForm> &qf ) ///< transformations of each layer
{
	assert(qf.size() == lcount());

	PntArr2f fwdPntPlaces(_preimage_area.x, _preimage_area.y);

	for (int l = lcount()-1; l >= 0; --l) {
		const QuadraticForm & f = qf[l];
		unsigned int x, y;
		for (y = 0; y < _preimage_area.y; ++y)
			for (x = 0; x < _preimage_area.x; ++x)
				fwdPntPlaces(x,y) = cnv(
					f(point<float>(point<int>(x,y)-_image_dspl))
					+ point<float>(_image_dspl));
		bilinearMap(fwdPntPlaces, _layers[l].preimage, NO_PREIMAGE);
	}
}

void layered_img::prev_frame(const QuadraticForm &qf)
{
	std::vector<QuadraticForm> vec;
	vec.push_back(qf);
	prev_frame(vec);
}

void layered_img::combine_layers(
	AImage &background,		///< image to be overwritten by moved layers
	const bitmap *mask,		///< points in image that may be overwritten, if mask=0 then everything may be overwritten
	bool noNoValue,			///< if true every initial layers' color is warped and transferred 
	int_point * displ)		///< out: displacement of background relative preimage top-left
{
	assert(!mask || background.Width() == mask->width() && background.Height() == mask->height());
	bitmap replace(background.Width(), background.Height());
	if (mask) {
		replace = *mask;
	} else {
		replace.fill(true);
	}

	int_point bDispl = displacement(size_point(background.Width(), background.Height()));
	if (displ)
		*displ = bDispl;
	
	for (int l = lcount()-1; l >= 0; --l)
		bilinearImage(
			background, replace, _layers[l].preimage, bDispl,
			_layers[l].initial_view, 
			displacement(size_point(
				_layers[l].initial_view.Width(), 
				_layers[l].initial_view.Height())) - _image_dspl,
			_no_value, _interpolate, noNoValue);
}

void layered_img::combine_layers(byte_image &background, 
	const bitmap *mask, bool noNoValue, int_point * displ)
{
	img::AImage a;
	share_data(a, background);
	combine_layers(a, mask, noNoValue, displ);
}

void layered_img::combine_ext_layer(byte_image &background, const byte_image &layer,
	const bitmap *mask, bool noNoValue, int_point * displ)
{
	img::AImage a;
	share_data(a, layer);

	_layers[0].initial_view.Swap(a);
	combine_layers(background, mask, noNoValue, displ);
	_layers[0].initial_view.Swap(a);
}

int_point layered_img::displacement(const size_point &layer_size) const
{
	int_point d;

	switch (_b) {
	case bind_top_left_corner: 
		d.x = 0;
		d.y = 0;
		break;
	case bind_top_right_corner:
		d.x = _preimage_area.x - layer_size.x;
		d.y = 0;
		break;
	case bind_bottom_left_corner: 
		d.x = 0;
		d.y = _preimage_area.y - layer_size.y;
		break;
	case bind_bottom_right_corner: 
		d.x = _preimage_area.x - layer_size.x;
		d.y = _preimage_area.y - layer_size.y;
		break;
	default:
		assert(false);
	}

	return d;
}

} //namespace img
